<!DOCTYPE html>
<html>
	<head>
		<!--
		  	作者：xujun_bachlor@163.com
	    	时间：2019-06-18
	    	描述：更多详细信息可以参考https://zhuanlan.zhihu.com/p/22163474
	    	code mirror官网： https://codemirror.net/
    -->
		<meta charset="utf-8" />
		<title>在线编辑器</title>
		<style>
			/**
			 * 各种样式可以打开开发者模式进行查看
			 */
			.CodeMirror {
				height: 100% !important;
				font-size: 14px;
				line-height: 18px;
			}
			#output {
				width: 100%;
				height: 100%;
			}
			#iframeResult {
				display:block;
				overflow:hidden;
				border:0!important;
				min-width:100px;
				width:100%;
				height:100%;
				background-color:#fff
			}
			.windowLabel {
				letter-spacing: 1px;
    		text-transform: uppercase;
		    border: 1px solid rgba(255,255,255,0);
		    background: rgba(243,245,246,.8);
		    display: inline-block;
		    position: absolute;
		    top: 7px;
		    right: 5px;
		    text-align: center;
		    font-size: 11px;
		    border-radius: 20px;
		    z-index: 30;
		    color: #7f94a1;
		    line-height: 23px;
		    padding: 0 7px;
			}
			.windowLabel .label {
				color: #23527c;
			}
			.label {
		    display: inline;
		    padding: .2em .6em .3em;
		    font-size: 75%;
		    font-weight: 700;
		    line-height: 1;
		    color: #fff;
		    text-align: center;
		    white-space: nowrap;
		    vertical-align: baseline;
		    border-radius: .25em;
			}
			.fa {
		    display: inline-block;
		    font: normal normal normal 14px/1 FontAwesome;
		    font-size: inherit;
		    text-rendering: auto;
		    -webkit-font-smoothing: antialiased;
		    -moz-osx-font-smoothing: grayscale;
			}
			.btn {
				display: inline-block;
		    font-weight: 400;
		    color: #4E5155;
		    text-align: center;
		    vertical-align: middle;
		    -webkit-user-select: none;
		    -moz-user-select: none;
		    -ms-user-select: none;
		    user-select: none;
		    background-color: transparent;
		    border: 1px solid transparent;
		    padding: .438rem 1.125rem;
		    font-size: .894rem;
		    line-height: 1.54;
		    border-radius: .25rem;
		    transition: all 0.2s ease-in-out;
		    cursor: pointer;
			}
			.btn-primary {
			  background: #26b4ff;
			}
			.btn-primary:hover {
			  background: #24abf2;
			}
			.btn-primary, .btn-primary:hover {
			  border-color: transparent;
			  color: #fff;
			}
			.btn-sm{
			  padding: .188rem .6875rem;
		    font-size: .75rem;
		    line-height: 1.5;
		    border-radius: .25rem;
			}
			.tool-group {
				margin: 10px;
				display: flex;
				align-items: center;
				height: 33px;
			}
			.error-tip {
				display: inline-block;
				color: #a94442;
		    background-color: #f2dede;
        padding: 6px;
        border: 1px solid #ebccd1;
    		border-radius: 4px;
			}
			/*
			 * 文本框中placeholder的样式
			 */
	    #splitContainer .cm-s-dracula.CodeMirror {
	    	color: rgba(248, 248, 242,0.3) !important;
	    }
	    #select {
		    height: 26px;
		    outline: none;
		    border-radius: 3px;
		    padding-left: 15px;
		    margin-left: 5px;
	    }
	    .file-btn {
				line-height: 22px;
				width: 270px;
				border: 1px solid #409EFF;
				margin-left: 20px;
			}
			.inputfile {
				width: 0.1px;
				height: 0.1px;
				opacity: 0;
				overflow: hidden;
				position: absolute;
				z-index: -1;
			}
			.inputfile + label {
				font-size: 14px;
				color: #409eff;
				background-color: #ecf5ff;
				border: 1px solid #b3d8ff;
				display: inline-block;
				cursor: pointer; /* 小手光标*/
				padding: 0 10px;
			}

			.inputfile:focus + label,
			.inputfile + label:hover {
				background: #409eff;
				border-color: #409eff;
				color: #fff;
			}
			.inputfile:focus + label {
				outline: 1px dotted #000;
				outline: -webkit-focus-ring-color auto 5px;
			}
		</style>
		<!-- code mirror的样式 -->
		<link rel="stylesheet" href="css/codemirror.css" />
		<!--<link rel="stylesheet" href="css/dialog.css" />-->
		<!-- 主题的样式 -->
		<link rel="stylesheet" href="theme/3024-day.css">
		<link rel="stylesheet" href="theme/3024-night.css">
		<link rel="stylesheet" href="theme/abcdef.css">
		<link rel="stylesheet" href="theme/ambiance.css">
		<link rel="stylesheet" href="theme/base16-dark.css">
		<link rel="stylesheet" href="theme/bespin.css">
		<link rel="stylesheet" href="theme/base16-light.css">
		<link rel="stylesheet" href="theme/blackboard.css">
		<link rel="stylesheet" href="theme/cobalt.css">
		<link rel="stylesheet" href="theme/colorforth.css">
		<link rel="stylesheet" href="theme/dracula.css">
		<link rel="stylesheet" href="theme/duotone-dark.css">
		<link rel="stylesheet" href="theme/duotone-light.css">
		<link rel="stylesheet" href="theme/eclipse.css">
		<link rel="stylesheet" href="theme/elegant.css">
		<link rel="stylesheet" href="theme/erlang-dark.css">
		<link rel="stylesheet" href="theme/gruvbox-dark.css">
		<link rel="stylesheet" href="theme/hopscotch.css">
		<link rel="stylesheet" href="theme/icecoder.css">
		<link rel="stylesheet" href="theme/isotope.css">
		<link rel="stylesheet" href="theme/lesser-dark.css">
		<link rel="stylesheet" href="theme/liquibyte.css">
		<link rel="stylesheet" href="theme/lucario.css">
		<link rel="stylesheet" href="theme/material.css">
		<link rel="stylesheet" href="theme/mbo.css">
		<link rel="stylesheet" href="theme/mdn-like.css">
		<link rel="stylesheet" href="theme/midnight.css">
		<link rel="stylesheet" href="theme/monokai.css">
		<link rel="stylesheet" href="theme/neat.css">
		<link rel="stylesheet" href="theme/neo.css">
		<link rel="stylesheet" href="theme/night.css">
		<link rel="stylesheet" href="theme/nord.css">
		<link rel="stylesheet" href="theme/oceanic-next.css">
		<link rel="stylesheet" href="theme/panda-syntax.css">
		<link rel="stylesheet" href="theme/paraiso-dark.css">
		<link rel="stylesheet" href="theme/paraiso-light.css">
		<link rel="stylesheet" href="theme/pastel-on-dark.css">
		<link rel="stylesheet" href="theme/railscasts.css">
		<link rel="stylesheet" href="theme/rubyblue.css">
		<link rel="stylesheet" href="theme/seti.css">
		<link rel="stylesheet" href="theme/shadowfox.css">
		<link rel="stylesheet" href="theme/solarized.css">
		<link rel="stylesheet" href="theme/the-matrix.css">
		<link rel="stylesheet" href="theme/tomorrow-night-bright.css">
		<link rel="stylesheet" href="theme/tomorrow-night-eighties.css">
		<link rel="stylesheet" href="theme/ttcn.css">
		<link rel="stylesheet" href="theme/twilight.css">
		<link rel="stylesheet" href="theme/vibrant-ink.css">
		<link rel="stylesheet" href="theme/xq-dark.css">
		<link rel="stylesheet" href="theme/xq-light.css">
		<link rel="stylesheet" href="theme/yeti.css">
		<link rel="stylesheet" href="theme/idea.css">
		<link rel="stylesheet" href="theme/darcula.css">
		<link rel="stylesheet" href="theme/yonce.css">
		<link rel="stylesheet" href="theme/zenburn.css">
		
		<!-- 代码折叠时的样式 -->
		<link rel="stylesheet" href="css/foldgutter.css" />
		<!-- 全屏时的样式 -->
		<link rel="stylesheet" href="css/fullscreen.css" />
		<!-- 唤出来的提示代码的样式 -->
		<link rel="stylesheet" href="css/show-hint.css" />
		
		<link rel="stylesheet" href="css/jqx.base.css" />
		
		<script src="js/jquery-3.4.1.min.js"></script>
		<!-- code mirror核心代码 -->
		<script src="js/codemirror.js"></script>
		<!-- javascript代码高亮样式 -->
		<script src="js/javascript.js"></script>
		<!-- 可以设置代码提示 -->
		<script src="js/anyword-hint.js"></script>
		<script src="js/show-hint.js"></script>
		
		<script src="js/dialog.js"></script>
		<!-- 代码折叠 -->
		<script src="js/brace-fold.js"></script>
		<script src="js/foldcode.js"></script>
		<script src="js/foldgutter.js"></script>
		<!--<script src="js/comment-fold.js"></script>-->
		
		<!-- 全屏显示，好像没什么用 -->
		<script src="js/fullscreen.js"></script>
		
		<!-- 搜索完成后光标会消失 -->
		<script src="js/searchcursor.js"></script>
		<!-- 给编辑器添加搜索功能 -->
		<script src="js/search.js"></script>
		<!-- 使用后编写css会有各种高亮颜色提示 -->
		<script src="js/css.js"></script>
		<!-- html中混合使用css和js时也会有样式适配  -->
		<script src="js/htmlmixed.js"></script>
		<!-- 允许textarea中使用placeholder -->
		<script src="js/placeholder.js"></script>
		<!-- 使用后html代码会有各种颜色提示 -->
		<script src="js/xml.js"></script>
		<!-- 自动闭合标签 -->
		<script src="js/closetag.js"></script>
		<!-- 自动闭合括号 -->
		<script src="js/closebrackets.js"></script>
		<!-- 光标放在括号右边时，自动高亮对应的括号 -->
		<script src="js/matchbrackets.js"></script>
		<!-- 鼠标点击时，自动匹配成对的标签，需要先引入xml-fold.js，要不然会报错，引入xml-fold可以折叠html代码 -->
		<script src="js/xml-fold.js"></script>
		<script src="js/matchtags.js"></script>
		
		<!-- 高亮光标所在的行 -->
		<script src="js/active-line.js"></script>
		
		<!-- 用于分割页面 -->
		<script src="js/jqxcore.js"></script>
		<script src="js/jqxsplitter.js"></script>
	</head>
	<body>
		<div class="tool-group">
			<span style="margin-right: 15px;">HTML/CSS/JS在线工具</span>
			<button onclick="generateHtmlTemplate()" class="btn btn-primary btn-sm" style="margin-right: 15px;">html模板</button>
			<button onclick="submitTryit()" class="btn btn-primary btn-sm" style="margin-right: 15px;">执行代码</button>
			<input type="checkbox" value="" id="isautorun"/>自动执行
			<span style="margin-left: 20px;">切换主题</span>
			<select id="select" onchange="selectTheme(event)">
				<option>default</option>
		    <option>3024-day</option>
		    <option>3024-night</option>
		    <option>abcdef</option>
		    <option>ambiance</option>
		    <option>base16-dark</option>
		    <option>base16-light</option>
		    <option>bespin</option>
		    <option>blackboard</option>
		    <option>cobalt</option>
		    <option>colorforth</option>
		    <option>darcula</option>
		    <option selected>dracula</option>
		    <option>duotone-dark</option>
		    <option>duotone-light</option>
		    <option>eclipse</option>
		    <option>elegant</option>
		    <option>erlang-dark</option>
		    <option>gruvbox-dark</option>
		    <option>hopscotch</option>
		    <option>icecoder</option>
		    <option>idea</option>
		    <option>isotope</option>
		    <option>lesser-dark</option>
		    <option>liquibyte</option>
		    <option>lucario</option>
		    <option>material</option>
		    <option>mbo</option>
		    <option>mdn-like</option>
		    <option>midnight</option>
		    <option>monokai</option>
		    <option>neat</option>
		    <option>neo</option>
		    <option>night</option>
		    <option>nord</option>
		    <option>oceanic-next</option>
		    <option>panda-syntax</option>
		    <option>paraiso-dark</option>
		    <option>paraiso-light</option>
		    <option>pastel-on-dark</option>
		    <option>railscasts</option>
		    <option>rubyblue</option>
		    <option>seti</option>
		    <option>shadowfox</option>
		    <option>solarized dark</option>
		    <option>solarized light</option>
		    <option>the-matrix</option>
		    <option>tomorrow-night-bright</option>
		    <option>tomorrow-night-eighties</option>
		    <option>ttcn</option>
		    <option>twilight</option>
		    <option>vibrant-ink</option>
		    <option>xq-dark</option>
		    <option>xq-light</option>
		    <option>yeti</option>
		    <option>yonce</option>
		    <option>zenburn</option>
			</select>
			<div class="file-btn">
				<input type="file" id="file" class="inputfile" onchange="addLibJs(event)"/>
				<label for="file">添加库</label>
				<span class="inner"></span>
			</div>
			<div class="error-tip" style="display: none;margin-left: 20px;"><span ></span></div>
		</div>
		<div id="splitContainer">
			<div>
				<div id="leftSplitter">
					<div>
						<textarea placeholder="输入 HTML 代码……" id="code-html" class="form-control" name="code" rows="11"></textarea>
						<a href="javascript:void(0);" class="windowLabel" data-panel="js">
							<span class="label">
								HTML
							</span>
							<i class="fa fa-cog">
							</i>
						</a>
					</div>
					<div>
						<textarea placeholder="输入 JavaScript 代码……" id="code-js" class="form-control" name="code" rows="10"></textarea>
						<a href="javascript:void(0);" class="windowLabel" data-panel="js">
							<span class="label">
								JavaScript
							</span>
							<i class="fa fa-cog">
							</i>
						</a>
					</div>
				</div>
			</div>
			<div>
				<div id="rightSplitter">
					<div>
						<textarea placeholder="输入 CSS 代码……" id="code-css" class="form-control" rows="11"></textarea>
						<a href="javascript:void(0);" class="windowLabel" data-panel="js">
							<span class="label">
								CSS
							</span>
							<i class="fa fa-cog">
							</i>
						</a>
					</div>
					<div id="output">
					</div>
				</div>
			</div>
		</div>
		<script type="text/javascript">
			$(() => {
				let winHeight = 0;
				if(window.innerHeight) {
					winHeight = window.innerHeight
				}else if((document.body) && document.body.clientHeight) {
					winHeight = document.body.clientHeight;
				}
				if(document.documentElement && document.documentElement.clientHeight) {
					winHeight = document.documentElement.clientHeight;
				}
				$('#isautorun').prop('checked',true);	// 是否在写代码时自动运行代码
				let height = (winHeight - 60) * 0.86;
				// 分割html、css、js代码区
				$('#splitContainer').jqxSplitter({ height: height, width: '100%', orientation: 'vertical', panels: [{ size: '50%' }, { size: '50%' }] });
    		$('#leftSplitter').jqxSplitter({ height: '100%', width: '100%', orientation: 'horizontal',  panels: [{ size: '50%' }, { size: '50%'}] });
    		$('#rightSplitter').jqxSplitter({ height: '100%', width: '100%', orientation: 'horizontal',  panels: [{ size: '50%' }, { size: '50%'}] });
			});
			// 使用code mirror生成编辑器时配置
			let cm_opt = {
				mode: 'text/html',	// 模式，表示对那种类型的代码进行样式渲染
				gutter: false,
				lineNumbers: true,	// 行号
				autoCloseBrackets:true,	// 是否自动闭合括号
				styleActiveLine: true,	// 高亮当前行
				theme:"dracula",	// 编辑器主题
				lineWrapping: true,	// 是否换行
				foldGutter: true,		// 是否能折叠代码
				gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
				extraKeys: {"Alt-/": "autocomplete"},	// 使用alt+/唤出代码提示
				matchBrackets: true,	// 是否能匹配成对的括号
				matchTags: {bothTags: true},	// 鼠标点击时匹配成对的标签
				autoCloseTags:true	// 是否自动闭合标签
			}
			
			/**
			 * 作者：xujun_bachlor@163.com
        	时间：2019-06-18
        	描述：更多配置信息可以参考https://zhuanlan.zhihu.com/p/22163474
			 */
			let html_editor = CodeMirror.fromTextArea(document.getElementById("code-html"),cm_opt);
			html_editor.on('change',function(inst,changes) {
				if($('#isautorun').prop('checked')) {
					submitTryit();
				}
			})
			// 文本换行时顶部对齐
//			let charWidth = html_editor.defaultCharWidth(), basePadding = 4;
//			html_editor.on("renderLine", function(cm, line, elt) {
//      var off = CodeMirror.countColumn(line.text, null, cm.getOption("tabSize")) * charWidth;
//      elt.style.textIndent = "-" + off + "px";
//      elt.style.paddingLeft = (basePadding + off) + "px";
//    });
//    html_editor.refresh();
      
			Object.assign(cm_opt,{mode: 'text/css'});
			let css_editor = CodeMirror.fromTextArea(document.getElementById("code-css"),cm_opt);
			css_editor.on('change',function(inst,changes) {
				if($('#isautorun').prop('checked')) {
					submitTryit();
				}
			})
			Object.assign(cm_opt,{mode: 'text/javascript'});
			let js_editor = CodeMirror.fromTextArea(document.getElementById("code-js"),cm_opt);
			js_editor.on('change',function(inst,changes) {
				if($('#isautorun').prop('checked')) {
					submitTryit();
				}
			})
			// 获取上传文件的url
			function getObjectURL(file) {  
				 var url = null;  
				 if (window.createObjcectURL != undefined) {  
						 url = window.createOjcectURL(file);  
				 } else if (window.URL != undefined) {  
						 url = window.URL.createObjectURL(file);  
				 } else if (window.webkitURL != undefined) {  
						 url = window.webkitURL.createObjectURL(file);  
				 }  
				 return url;  
		 }
			function addLibJs(e) {
					let _file = e.target.files[0];
					//实例化fileReader,
					let fd = new FileReader();
					//获取当前选择文件的类型
					let fileType = _file.type;
					//调它的readAsDataURL并把原生File对象传给它，
					fd.readAsDataURL(_file);
					let html = html_editor.getValue();
					fd.onload = function() {
						if(html && /^text\/javascript$/.test(fileType)) {
							$('.inner').text(_file.name);
							let url = getObjectURL(_file);
							let extra = `<!-- ${_file.name} -->\r\n<script src="${url}"><\/script>\r\n`;
							html = extra + html;
							html_editor.setValue(html);
						}
					}
				}
			// 执行所有编辑器的代码生成页面,代码可自己调试查看结果
			function submitTryit() {
				// 获取编辑器内容
				let html = html_editor.getValue();
				let css = css_editor.getValue();
				let js = js_editor.getValue();
				
				let src = html;
				let array_matches_head_tag = null;
				let array_matches_body_tag = null;
				
				if(html) {
					let patternHtmlTag = /<html([^>]*)>/im;
					let array_matches_html_tag = patternHtmlTag.exec(html);
					if(array_matches_html_tag) {
						src = src.replace('<html>','<html'+array_matches_html_tag[1]+'>');
					}
					let patternHead = /<head[^>]*>((.|[\n\r])*)<\/head>/im;
					array_matches_head_tag = patternHead.exec(src);
					let patternBody = /<body([^>]*)>/im;
					array_matches_body_tag = patternBody.exec(src);
					if(array_matches_body_tag) {
						src = src.replace('<body>', '<body ' + array_matches_body_tag[1] + '>');
					}
				}
				if(~css.indexOf('<style>')) {
					$('.error-tip span').text('CSS 编辑框不需要 <style> 标签');
					$('.error-tip').show().delay(5000).fadeOut();
					return;
				}else if(~js.indexOf('<script>')) {
					$('.error-tip span').text('JS 编辑框不需要 <script> 标签');
					$('.error-tip').show().delay(5000).fadeOut();
					return;
				}else {
					$('.error-tip').hide();
				}
				if(css) {
					css = '<style>'+css+'</style>';
					if(array_matches_head_tag) {
						src = src.replace('</head>',css+'</head>');
					}else if(array_matches_body_tag) {
						src = src.replace('</body>',css+'</body>');
					}else {
						src += css;
					}
				}
				if(js) {
					js = '<script>'+js+'<\/script>';
					if(array_matches_body_tag) {
						src = src.replace('</body>',js+'</body>');
					}else {
						src += js;
					}
				}
				
				let text = src;
				let ifr = document.createElement('iframe');
				ifr.setAttribute("frameborder", "0");
				ifr.setAttribute("id", "iframeResult");
				document.getElementById("output").innerHTML = "";
				document.getElementById("output").appendChild(ifr);
				var ifrw = (ifr.contentWindow) ? ifr.contentWindow: (ifr.contentDocument.document) ? ifr.contentDocument.document: ifr.contentDocument;
				ifrw.document.open();
				ifrw.document.write(text);
				ifrw.document.close();
				autodivheight();
			}
			// 生成默认的html模板
			function generateHtmlTemplate() {
				let html_code = "<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<title>文档标题</title>\n</head>\n<body>\n\t<h1>我的第一个HTML页面</h1>\n\t<p>我的第一个段落。</p>\n</body>\n</html>\n";
				html_editor.getDoc().setValue(html_code);
			}
			
			window.addEventListener("resize", autodivheight);
			function autodivheight(){
			    var winHeight=0;
			    if (window.innerHeight) {
			        winHeight = window.innerHeight;
			    } else if ((document.body) && (document.body.clientHeight)) {
			        winHeight = document.body.clientHeight;
			    }
			    //通过深入Document内部对body进行检测，获取浏览器窗口高度
			    if (document.documentElement && document.documentElement.clientHeight) {
			        winHeight = document.documentElement.clientHeight;
			    }
			    height = height = (winHeight - 60) * 0.86;
			    document.getElementById("splitContainer").style.height= height +"px";
			}
			
			// 切换主题
			function selectTheme(e) {
				html_editor.setOption('theme',e.target.value);
				css_editor.setOption('theme',e.target.value);
				js_editor.setOption('theme',e.target.value);
			}
		</script>
	</body>
</html>
